package apple.ui.swt;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import apple.core.Apple;
import apple.ui.IMonitor;

public class Monitor extends Canvas implements IMonitor {

   static Logger logger = Logger.getLogger(Monitor.class);

   private ImageData imageData;

   private Apple apple;

   private static int[] appleColors = new int[] { SWT.COLOR_BLACK,
         SWT.COLOR_MAGENTA, SWT.COLOR_DARK_BLUE, SWT.COLOR_BLUE,
         SWT.COLOR_DARK_GREEN, SWT.COLOR_GRAY, SWT.COLOR_BLUE, SWT.COLOR_BLUE,
         SWT.COLOR_BLACK, SWT.COLOR_RED, SWT.COLOR_GRAY, SWT.COLOR_RED,
         SWT.COLOR_GREEN, SWT.COLOR_YELLOW, SWT.COLOR_BLUE, SWT.COLOR_WHITE };

   private static ImageData createIndexedColorImageData(Display display) {

      List<RGB> rgbList = new ArrayList<RGB>(0x10);

      for (int appleColor : appleColors) {
         Color color = display.getSystemColor(appleColor);
         RGB rgb = color.getRGB();
         rgbList.add(rgb);
      }

      RGB[] rgbArray = rgbList.toArray(new RGB[0]);
      PaletteData palette = new PaletteData(rgbArray);
      ImageData imageData = new ImageData(280, 192, 8, palette);

      return imageData;
   }

   public Monitor(final Composite parent) {
      super(parent, SWT.NO_BACKGROUND);
      setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
      imageData = createIndexedColorImageData(getDisplay());

      addPaintListener(new PaintListener() {
         public void paintControl(PaintEvent event) {
            if (event.x < IMonitor.BORDER_HORIZONTAL_SIZE
                  || event.y < IMonitor.BORDER_VERTICAL_SIZE
                  || event.x + event.width > IMonitor.BORDER_HORIZONTAL_SIZE
                        + IMonitor.CONTENT_HORIZONTAL_SIZE
                  || event.y + event.height > IMonitor.BORDER_VERTICAL_SIZE
                        + IMonitor.CONTENT_VERTICAL_SIZE) {
               event.gc.fillRectangle(0, 0, IMonitor.TOTAL_HORIZONTAL_SIZE,
                     IMonitor.TOTAL_VERTICAL_SIZE);
               System.out.format(
                     "Out of bounds: x=%d, y=%d, width=%d, height=%d%n",
                     event.x, event.y, event.width, event.height);
            }
            Image image = new Image(getDisplay(), imageData);
            event.gc.drawImage(image, 0, 0, imageData.width, imageData.height,
                  IMonitor.BORDER_HORIZONTAL_SIZE,
                  IMonitor.BORDER_VERTICAL_SIZE,
                  IMonitor.CONTENT_HORIZONTAL_SIZE,
                  IMonitor.CONTENT_VERTICAL_SIZE);
            image.dispose();
         }
      });

      addDisposeListener(new DisposeListener() {
         public void widgetDisposed(DisposeEvent e) {
            if (apple != null) {
               apple.setMonitor(null);
               apple = null;
            }
         }
      });
   }

   public void setApple(Apple apple) {
      this.apple = apple;
   }

   public Point computeSize(int wHint, int hHint, boolean changed) {
      return new Point(IMonitor.TOTAL_HORIZONTAL_SIZE,
            IMonitor.TOTAL_VERTICAL_SIZE);
   }

   public String toString() {
      return COLOR_MONITOR_SHORT_NAME;
   }

   /*
    * (non-Javadoc)
    * 
    * @see apple.IMonitor#drawImage(int[][])
    * 
    * This method must not be called after this widget has been disposed.
    */
   public void drawImage(int[][] pixelData) {
      for (int x = 0; x < 280; x++) {
         for (int y = 0; y < 192; y++) {
            imageData.data[y * imageData.bytesPerLine + x] = (byte) pixelData[y][x];
         }
      }
      getDisplay().asyncExec(new Runnable() {
         public void run() {
            if (!isDisposed()) {
               redraw(IMonitor.BORDER_HORIZONTAL_SIZE,
                     IMonitor.BORDER_VERTICAL_SIZE,
                     IMonitor.CONTENT_HORIZONTAL_SIZE,
                     IMonitor.CONTENT_VERTICAL_SIZE, true);
            }
         }
      });
   }
}
